package com.lightsh.download;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.NameValuePair;

import com.lightsh.network.Utility;

import android.content.Context;
import android.os.Process;
import android.util.Log;
import android.util.Pair;

/**
 * 文件上传辅助类，用于向服务器上传文件,支持断点续传
 * @author Shu
 *
 */
public class FileUploadHelper {
    
	
	private static final boolean DEBUG = true;
	private static final String TAG = "FileUploadHelper";
	/**
	 * 总尝试次数
	 */
	private static final int UPLOAD_TRY_COUNT = 1;
	
	/**
	 * 任务列表,并用作同步锁
	 */
	private List<UploadInfo> mTaskList;
	
	public static final int STATE_INIT = 0;
	public static final int STATE_CANCEL = -1;
	public static final int STATE_UPLOADING = 2;
	public static final int STATE_ERROR = -2;
	public static final int STATE_FINISH = 3;
	public static class UploadInfo{
		String mUrl;
		String mUploadFileFullPath;
		NameValuePair[] mParams;
		List<Pair<String, String>> mHttpHeaders;
		int mPercent;
		int mState;
		String mResponse;
		long mRang;
		String mKey;
		public UploadInfo(String key, String url, String fullPath, NameValuePair[] postParams, List<Pair<String, String>> headers, int rang){
			mKey = key;
			mUrl = url;
			mUploadFileFullPath = fullPath;
			mParams = postParams;
			mHttpHeaders = headers;
			mState = STATE_INIT;
			mPercent = 0;
			mResponse = null;
			mRang = rang;
		}
	}
	/**
	 * 取消上传
	 * @param packageName
	 */
	public void cancelUpload(String packageName){
		synchronized (mTaskList) {
			for(int i = 0; i < mTaskList.size(); ++i){
				if(mTaskList.get(i).mKey.equals(packageName)){
					mTaskList.get(i).mState = STATE_CANCEL;
					mTaskList.get(i).mResponse = "user cancel";
				}
			}
		}
	}
	/**
	 * 取消所有上传
	 */
	public void cancelAll(){
		synchronized (mTaskList) {
			for(int i = 0; i < mTaskList.size(); ++i){
				mTaskList.get(i).mState = STATE_CANCEL;
				mTaskList.get(i).mResponse = "user cancel";
			}
		}
	}
	
	/**
	 * 上传回调接口
	 * 注意不要在该回调中做复杂操作
	 *
	 */
	public static interface UploadStateChangeListener {
		/**
		 * 注意：在该回调中，尽量不能调用使用锁的代码段，否则会造成死锁情况!
		 * @param key
		 * @param state
		 * @param percent
		 * @param response
		 * @param extraData    这里传入md5值
		 */
		public void onUploadStateChange(String key, int state, int percent, String response, String extraData);
	}
	private UploadStateChangeListener mListener;
	
	/**
	 * 任务列表,并用作同步锁
	 */
//	private List<UploadInfo> mTaskList;
	
	public FileUploadHelper(Context context, UploadStateChangeListener listener){
		mListener = listener;
		mTaskList = new ArrayList<UploadInfo>();
	}
	/**
	 * 进行上传操作
	 * FIXME 该方法没有进行线程的队列控制
	 * @param info 需要上传的数据结构
	 */
	public void uploadFile(final UploadInfo info){
		Thread uploadThread = new Thread(new Runnable() {
			public void run() {
				Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
				final UploadInfo uploadInfo = info;
				synchronized (mTaskList) {
					mTaskList.add(uploadInfo);
				}				
				// 进行上传
				if(uploadInfo.mState == STATE_CANCEL){
					uploadInfo.mResponse = "user cancel";
					notifyStateChange(uploadInfo);
					return;
				}
				doUpload(uploadInfo);
			}
		});
		uploadThread.start();
	}
	
	
    /**
     * 上传文件到服务器
     * @param url 			服务器地址
     * @param uploadFile	需要上传的文件
     */
    private void doUpload(UploadInfo uploadInfo) {
    	File uploadFile = new File(uploadInfo.mUploadFileFullPath);
    	if(!uploadFile.exists()){
    		uploadInfo.mState = STATE_ERROR;
    		notifyStateChange(uploadInfo);
    		return;
    	}
    	
    	uploadInfo.mState = STATE_UPLOADING;
    	HttpURLConnection conn = null;
    	
    	long startPos = uploadInfo.mRang;				//< 开始传输位置
    	long nTotal = uploadFile.length();				//< 文件总长度
    	if(startPos > nTotal){
    		startPos = nTotal;
    	}
    	
    	InputStream inStream = null;
    	OutputStream outStream = null;
    	FileInputStream fileInStream = null;
    	try{
    		fileInStream = new FileInputStream(uploadFile);
    		try{
    			int nTry = 0;
    			for(; nTry < UPLOAD_TRY_COUNT; ++nTry){
    				conn = Utility.getConnection(uploadInfo.mUrl);
    				
    				// 如果上传文件很大，服务器的返回时间较长
    				conn.setReadTimeout(1000 * 60 * 3);
    				try{
    					conn.setDoInput(true);
        				conn.setDoOutput(true);
        				conn.setUseCaches(false);
        				conn.setRequestMethod("POST");
        			    conn.setRequestProperty("Connection", "Keep-Alive");
        			    conn.setRequestProperty("Charset", "UTF-8"); 
        			    conn.setRequestProperty("Accept", "*/*");
        			    if(uploadInfo.mHttpHeaders != null && uploadInfo.mHttpHeaders.size() > 0){
        			    	for(Pair<String, String> header : uploadInfo.mHttpHeaders){
        			    		conn.setRequestProperty(header.first, header.second);
        			    	}
        			    }
        			    final String BOUNDARY = "-----AbCdEfGhIjK";
        			    final String CRLF = "\r\n";
        			    
        			    conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
        			    StringBuilder builder = new StringBuilder();
        			    
        			    // 开头的数据
        			    builder.append("--").append(BOUNDARY).append(CRLF);
        			    builder.append("Content-Disposition: form-data; name=\"parameters\"").append(CRLF);
        			    builder.append("Content-Type: param").append(CRLF + CRLF);
        			    
        			    StringBuilder paramBuilder = new StringBuilder();
        			    if(uploadInfo.mParams != null && uploadInfo.mParams.length > 0){
        			    	for(int i = 0; i < uploadInfo.mParams.length; ++i){
        			    		paramBuilder.append(uploadInfo.mParams[i].getName()).append("=").append(uploadInfo.mParams[i].getValue());
        			    		if(i != (uploadInfo.mParams.length - 1)){
        			    			paramBuilder.append("&");
        			    		}
        			    	}
        			    }
        			    
        			    
        			    builder.append(paramBuilder.toString()).append(CRLF);
        			    
        			    // 文件数据
        			    builder.append("--").append(BOUNDARY).append(CRLF);
        			    builder.append("Content-Disposition: form-data; name=\"apk\"; filename=\"apk\"").append(CRLF);
        			    builder.append("Content-Type: apk").append(CRLF + CRLF);
        			    
        			    byte[] startBytes = builder.toString().getBytes();
        			    byte[] endBytes = (CRLF + "--" + BOUNDARY + "--" + CRLF).getBytes();
        			    
        			    long contentLength = startBytes.length + nTotal - startPos + endBytes.length;
        			    
//        			    conn.setRequestProperty("Content-Length", String.valueOf(contentLength));
        			    // 如果写入Content-Length，conn.getOutputStream()会分配Content-Length长度的内存，可能造成OutOfMemoryException！
        			    // 使用这种方式可以防止分配过多内存,但不知道会不会造成其他问题
        			    conn.setFixedLengthStreamingMode((int)contentLength);
//        			    conn.setRequestProperty("Transfer-Encoding", "chunked");

//        			    if(DEBUG){
//        			    	Log.e(Constants.TAG, new String(startBytes));
//            			    Log.e(Constants.TAG, new String(endBytes));
//        			    }
        			    
        			    outStream = conn.getOutputStream();
        			    
        			    outStream.write(startBytes);
        			    
        			    byte[] buffer = new byte[4096];
        			    int nRead = 0;
        			    long nCount = 0;
        			    int prevPercent = -1;
        			    if(startPos > 0){
        			    	fileInStream.skip(startPos);
        			    	nCount = startPos;
        			    }
        			    
        			    long timeTickLast = System.currentTimeMillis(); 		// < 记录刷新进度时间
        			    while(nRead != -1 && nCount < nTotal){
        			    	if(uploadInfo.mState == STATE_CANCEL){
        			    		break;
        			    	}        			    	
        			    	nRead = fileInStream.read(buffer);
        			    	outStream.write(buffer, 0, nRead);
        			    	nCount += nRead;
        			    	startPos += nRead;
//        			    	Log.e(Constants.TAG, "" + (startBytes.length + nCount));
        			    	
        			    	int percent = (int) (nCount * 100 / nTotal);
//        			    	Log.e(Constants.TAG, "" + percent);
        			    	if (percent > prevPercent) {
								prevPercent = percent;
								uploadInfo.mPercent = percent;
								// 判断刷新时间间隔是否太短
								long timeTickNow = System.currentTimeMillis();
								if (timeTickNow - timeTickLast > 300 || percent == 0 || percent == 100) {
									timeTickLast = timeTickNow;
									notifyStateChange(uploadInfo);
								}
							}
        			    }
        			    outStream.write(endBytes);
        			    outStream.flush();
        			    
        			    if(uploadInfo.mState == STATE_CANCEL){
        			    	// 取消
        			    	notifyStateChange(uploadInfo);
        			    	try{
        			    		outStream.close();
        			    	}catch(Exception e){
        			    		if(DEBUG){
        			    			e.printStackTrace();
        			    		}
        			    	}
        			    	return;
        			    }else{
        			    	if(nCount < nTotal){
        			    		// 文件出错
        			    		outStream.close();
        			    		throw new IOException("Upload file error: file size not right!");
        			    	}else{
			    				int responseCode = conn.getResponseCode();
                		        if(null == (inStream = conn.getErrorStream())){
                		        	inStream = conn.getInputStream();
                		        }
        			    		BufferedReader br = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
        			    		StringBuilder sb = new StringBuilder();
        			    		String line;
        			    		while (null != (line = br.readLine())) {
        		                    sb.append(line).append("\n");
        		                }
        			    		if(responseCode != HttpURLConnection.HTTP_OK){
			    					throw new Exception("server responseCode :" + responseCode);
			    				}else{
			    					uploadInfo.mResponse = sb.toString();
			    					if(DEBUG){
			    						Log.e(TAG, "response:" + uploadInfo.mResponse);
			    					}			    					
	        			    		inStream.close();
	        			    		outStream.close();
	        			    		uploadInfo.mState = STATE_FINISH;
	        			    		notifyStateChange(uploadInfo);
	        			    		return;
			    				}
        			    	}
        			    }
        			    
        			    
    				}catch(Exception e){
    					if(DEBUG){
    						e.printStackTrace();
    					}
    					try {
							Thread.sleep(2000);
						} catch (InterruptedException e1) {
							if(DEBUG){
								e1.printStackTrace();
							}							
						}
    					continue;
    				}finally{
    					conn.disconnect();
    				}
    			}
    			// 这里应该是必须回调的
    			if(nTry >= UPLOAD_TRY_COUNT){
    				// 超过最大尝试次数,失败
    				uploadInfo.mState = STATE_ERROR;
    				notifyStateChange(uploadInfo);
    				return;
    			}else{
    				throw new RuntimeException("end with no notify!");
    			}
    		}finally{
    			fileInStream.close();
    		}
    	}catch(IOException e){
    		if(DEBUG){
    			Log.e(TAG, "upload file exception:");
    			e.printStackTrace();
    		}
    		uploadInfo.mState = STATE_ERROR;
    		notifyStateChange(uploadInfo);
    		return;
    	}
    }
    /**
     * 通知状态改变
     * @param UploadInfo
     */
    private void notifyStateChange(UploadInfo uploadInfo) {
    	synchronized (mTaskList) {
    		if(mListener != null){
    			String md5 = null;
    			if(uploadInfo.mParams != null && uploadInfo.mParams.length > 0){
    				md5 = uploadInfo.mParams[0].getValue();
    			}
    			mListener.onUploadStateChange(uploadInfo.mKey, uploadInfo.mState, uploadInfo.mPercent, 
    					uploadInfo.mResponse, md5);
    		}
    		
    		// 判断是否需要从列表中删除
			if(uploadInfo.mState == STATE_ERROR || uploadInfo.mState == STATE_CANCEL || uploadInfo.mState == STATE_FINISH){
				for(int i = 0; i < mTaskList.size() ; ++i){
					if(mTaskList.get(i) == uploadInfo){
						mTaskList.remove(i);
						break;
					}
				}
			}
    		
		}
	}
    
}